﻿<#
	File      = Get-WmiClass.ps1
	Author    = Kirk "Poshoholic" Munro
	Publisher = Poshoholic Studios
	Copyright = © 2012 Poshoholic Studios. All rights reserved.
#>

function Get-WmiClass {
	# .ExternalHelp Get-WmiClass-Help.xml
	[CmdletBinding()]
	[OutputType('[System.Management.ManagementClass]')]
	param(
		[Parameter()]
		[System.Management.Automation.SwitchParameter]
		$IncludeDerivedClasses,

		[Parameter(Position=1)]
		[System.String[]]
		$Property,

		[Parameter(Position=0)]
		[ValidateNotNullOrEmpty()]
		[System.String[]]
		$Name,

		[Parameter()]
		[System.Management.Automation.SwitchParameter]
		$Amended,

		[Parameter()]
		[System.Management.Automation.SwitchParameter]
		$DirectRead,

		[Parameter()]
		[System.Management.Automation.SwitchParameter]
		$AsJob,

		[Parameter()]
		[System.Management.ImpersonationLevel]
		$Impersonation,

		[Parameter()]
		[System.Management.AuthenticationLevel]
		$Authentication,

		[Parameter()]
		[System.String]
		$Locale,

		[Parameter()]
		[System.Management.Automation.SwitchParameter]
		$EnableAllPrivileges,

		[Parameter()]
		[System.String]
		$Authority,

		[Parameter()]
		[System.Management.Automation.Credential()]
		$Credential = [System.Management.Automation.PSCredential]::Empty,

		[Parameter()]
		[System.Int32]
		$ThrottleLimit,

		[Parameter()]
		[ValidateNotNullOrEmpty()]
		[Alias('Cn')]
		[System.String[]]
		$ComputerName,

		[Parameter()]
		[Alias('NS')]
		[System.String]
		$Namespace
	)
	begin {
		try {
			#region Workaround for PowerShell 3.0 cmdlet reorg.

			<#
			PowerShell 3.0 Breaking Change:
			In PowerShell 2.0, Out-Null was part of the Microsoft.PowerShell.Utility snapin. With the
			move to PowerShell 3.0 and the switch to modules, the PowerShell team decided it was more
			appropriate to have this cmdlet be included in the Microsoft.PowerShell.Core snapin so that
			it continues to be available out of the box without loading any modules. This shift means
			that fully-qualified calls to Microsoft.PowerShell.Utility\Out-Null will no longer work in
			PowerShell 3.0 without using an alias.
			#>

			if ((Microsoft.PowerShell.Core\Get-Command -Module Microsoft.PowerShell.Core -Name Out-Null -ErrorAction SilentlyContinue) -and
			    (-not (Microsoft.PowerShell.Utility\Get-Alias -Name Microsoft.PowerShell.Utility\Out-Null -ErrorAction SilentlyContinue))) {
				Microsoft.PowerShell.Utility\New-Alias -Name Microsoft.PowerShell.Utility\Out-Null -Value Microsoft.PowerShell.Core\Out-Null
			}

			#endregion

			#region Ensure that objects are sent through the pipeline one at a time.

			$outBuffer = $null
			if ($PSCmdlet.MyInvocation.BoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
				$PSCmdlet.MyInvocation.BoundParameters['OutBuffer'] = 1
			}

			#endregion

			#region Add empty credential support, regardless of the function being proxied.

			if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Credential') -and ($Credential -eq [System.Management.Automation.PSCredential]::Empty)) {
				$PSCmdlet.MyInvocation.BoundParameters.Remove('Credential') | Microsoft.PowerShell.Utility\Out-Null
			}

			#endregion

			#region Look up the command being proxied.            

			$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-WmiObject',[System.Management.Automation.CommandTypes]::Cmdlet)

			#endregion

			#region If the command was not found, throw an appropriate command not found exception.

			if (-not $wrappedCmd) {
				[System.String]$message = $PSCmdlet.GetResourceString('DiscoveryExceptions','CommandNotFoundException')
				[System.Management.Automation.CommandNotFoundException]$exception = Microsoft.PowerShell.Utility\New-Object -TypeName System.Management.Automation.CommandNotFoundException -ArgumentList ($message -f 'Get-WmiObject')
				$exception.CommandName = 'Get-WmiObject'
				[System.Management.Automation.ErrorRecord]$errorRecord = Microsoft.PowerShell.Utility\New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $exception,'DiscoveryExceptions',([System.Management.Automation.ErrorCategory]::ObjectNotFound),'Get-WmiObject'
				$PSCmdlet.ThrowTerminatingError($errorRecord)
			}

			#endregion

			#region Set the default parameter values.

			if ($PSCmdlet.MyInvocation.MyCommand.ParameterSets.Count -eq 1) {
				if (-not $PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Class')) {
					$PSCmdlet.MyInvocation.BoundParameters['Class'] = $Class = 'meta_class'
				}
			} else {
				$parameterSet = $wrappedCmd.ParameterSets | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $PSCmdlet.ParameterSetName}
				if (-not $PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Class')) {
					$Class = 'meta_class'
					if (@($parameterSet.Parameters | Microsoft.PowerShell.Utility\Select-Object -ExpandProperty Name) -contains 'Class') {
						$PSCmdlet.MyInvocation.BoundParameters['Class'] = $Class
					}
				}
			}

			#endregion

			#region Initialize the extended pipeline.

			[System.String]$preProcessProxyCommand = ''
			[System.String]$processProxyCommand = ''
			[System.Management.Automation.ScriptBlock[]]$beforePipelineScriptBlocks = @()
			[System.Management.Automation.ScriptBlock[]]$afterPipelineScriptBlocks = @()
			if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Name')) {
			    $filters = @()
			    foreach ($className in $Name) {
			        if ($className -match '[\*\?]') {
			            $filters += ConvertTo-WmiFilter -PropertyName __CLASS -FilterValue $className
			        } else {
			            if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('IncludeDerivedClasses') -and $IncludeDerivedClasses) {
			                $filters += "(__this isa '$className')"
			            } else {
			                $filters += "(__this isa '$className' AND __CLASS='$className')"
			            }
			        }
			    }
			    $PSCmdlet.MyInvocation.BoundParameters['Filter'] = $filter = $filters -join ' OR '
			}

			#endregion

			#region Process parameters, extending the pipeline accordingly.

			if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Name')) {
				$PSCmdlet.MyInvocation.BoundParameters.Remove('Name') | Microsoft.PowerShell.Utility\Out-Null

			}
			if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('IncludeDerivedClasses')) {
				$PSCmdlet.MyInvocation.BoundParameters.Remove('IncludeDerivedClasses') | Microsoft.PowerShell.Utility\Out-Null

			}

			#endregion

			#region Create the proxy command script block.

			$scriptCmd = {& $wrappedCmd @PSBoundParameters}

			#endregion

			#region Append our extended pipeline commands to the wrapped command script block.

			if ($preProcessProxyCommand -and $processProxyCommand) {
				$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(($preProcessProxyCommand + [string]$scriptCmd + $processProxyCommand))
			} elseif ($preProcessProxyCommand) {
				$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(($preProcessProxyCommand + [string]$scriptCmd))
			} elseif ($processProxyCommand) {
				$scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(([string]$scriptCmd + $processProxyCommand))
			}

			#endregion

			#region Invoke parameter initialization code before the pipeline is run.

			foreach ($scriptBlock in $beforePipelineScriptBlocks) {
				& $scriptBlock
			}

			#endregion

			#region Use the script block to create the steppable pipeline, then invoke its begin block.

			$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
			$steppablePipeline.Begin($PSCmdlet)

			#endregion
		} catch {
			throw
		}
	}
	process {
		try {
			#region Process the element that was just received from the previous stage in the pipeline.

			$steppablePipeline.Process($_)

			#endregion
		} catch {
			throw
		}
	}
	end {
		try {
			#region Close the pipeline.

			$steppablePipeline.End()

			#endregion

			#region Invoke parameter finalizing code after the pipeline has been closed.

			foreach ($scriptBlock in $afterPipelineScriptBlocks) {
				& $scriptBlock
			}

			#endregion
		} catch {
			throw
		}
	}
}
